package pl.agh.pp.zephyr.logic;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import pl.agh.pp.zephyr.integration.GkController;
import pl.agh.pp.zephyr.integration.H323Controller;
import pl.agh.pp.zephyr.integration.McuController;
import pl.agh.pp.zephyr.integration.OpenMcuController;
import pl.agh.pp.zephyr.model.H323Area;
import pl.agh.pp.zephyr.utils.LogPublisher;

/**
 * Obiekt pełniący rolę mediatora pomiędzy kontrolerami elementów domeny H323 a
 * modelem danych. Operowanie na modelu <code>area</code> nie powinno odbywać
 * się bezpośrednio. TODO zastanować się czy w ogóle nie ukryć referencji do
 * "area" i ew. delegować potrzebne metody
 * */
public class Engine extends LogPublisher implements Serializable {

	private static final long serialVersionUID = 1L;

	public Engine(H323Area area) {
		super(Engine.class);
		this.area = area;
	}

	public final H323Area area;

	public final Set<GkController> gkcs = Collections
			.synchronizedSet(new HashSet<GkController>());

	public final Set<McuController> mcucs = Collections
			.synchronizedSet(new HashSet<McuController>());

	public GkController registerGk(String host, int port, String username,
			String password) throws Exception {
		log.info("registing gatekeeper controller on: " + host + ":" + port
				+ "...");
		if (host == null || "".equals(host)) {
			throw new IllegalArgumentException("host incorrect: " + host);
		}
		for (GkController t : gkcs) {
			if (t.getHost().equals(host) && ("" + port).equals(t.getPort())) {
				throw new Exception(
						"Gatekeeper on specified host already registered.");
			}
		}
		GkController g = SpringFactory.getBean(GkController.class);
		try {
			g.init(host, port, username, password);
			gkcs.add(g);
			area.registerGatekeeper(host, port + "", g);
			log.info("gatekeeper controller registered successfully...");
			return g;
		} catch (Exception e) {
			log.info("unable to register gatekeeper controller: "
					+ e.getMessage());
			throw e;
		}
	}

	public McuController registerMcu(String host, int port, String prefix, String httpPort) throws Exception {
		log.info("registing mcu controller on: " + host + ":" + port + "...");
		if (host == null || "".equals(host)) {
			throw new IllegalArgumentException("host incorrect: " + host);
		}
		for (McuController t : mcucs) {
			if (t.getHost().equals(host) && ("" + port).equals(t.getPort())) {
				throw new Exception("Mcu on specified host already registered.");
			}
		}
		OpenMcuController g = SpringFactory.getBean(OpenMcuController.class);
		// tak na prawde to powinno się pobrać implementację
		// odpowiednią do miksera
		// TODO, jakiś mechanizm rozstrzygania z jakim mikserem mamy do
		// czynienia, no - z fabryki
		try {
			g.setHost(host);
			g.setPort(port+"");
			g.setPrefix(prefix);
			g.setHttpPort(httpPort);
			g.init();
			mcucs.add(g);
			area.registerMcu(g.getHost(), g.getPort(), g.getPrefix(), g.getHttpPort(), g);
			log.info("mcu controller registered successfully...");
			return g;
		} catch (Exception e) {
			log.info("unable to register mcu controller: " + e.getMessage());
			throw e;
		}
	}

	public Set<GkController> getGkcs() {
		return gkcs;
	}

	public Set<McuController> getMcucs() {
		return mcucs;
	}

	/*
	 * public H323UnitController getController(Long id) {
	 * log.info("trying to get controller with id: " + id + "...");
	 * log.info("currently registered controllers: " + gkcs.size()); for
	 * (H323UnitController c : gkcs) { log.info("analyzing gatekeeper with id: "
	 * + c.getId()); if (c.getId().equals(id)) { return c; } } for
	 * (H323Controller c : mcucs) { log.info("analyzing mcu with id: " +
	 * c.getId()); if (c.getId().equals(id)) { return c; } } return null; }
	 */
	public void unregisterController(H323Controller con) {
		log.info(this + "");
		log.info("trying to unregister controller: " + con + "...");
		String host = null, port = null;
		if (con instanceof GkController && gkcs.remove(con)) {
			GkController t = (GkController) con;
			host = t.getHost();
			port = t.getPort();
			t.destroy();
			log.info("removed from gatekeepers");
		} else if (con instanceof McuController && mcucs.remove(con)) {
			McuController t = (McuController) con;
			host = t.getHost();
			port = t.getPort();
			t.destroy();
			log.info("removed from mcus");
		} else {
			log.info("failed to remove controller - not found");
			return;
		}
		log.info("removed from model: "
				+ area.removeElement(host, port));
	}
	
}
